# 2024-3-6启动开发多线程版本

# 主要增加交易频道喊话功能

from DrissionPage import ChromiumPage  # 导入浏览器类

from DrissionPage._elements.chromium_element import ChromiumElement  # 导入element类型

import re, time, pysnooper, random, logging

from DrissionPage.common import Actions

from DrissionPage.errors import ElementNotFoundError

from datetime import datetime

from concurrent.futures import ThreadPoolExecutor, futures_wait

from threading import Lock

from DrissionPage._pages.chromium_tab import ChromiumTab


logger = logging.getLogger(__name__)

logging.basicConfig(

    filename="example.log",

    encoding="utf-8",

    level=logging.INFO,

    format="%(threadName)s %(asctime)s %(levelname)s %(message)s",

    datefmt="%m/%d %H:%M:%S",
)

# from DrissionPage import ChromiumOptions


# co = ChromiumOptions()

# co.set_timeouts(page_load=60)

tLock = Lock()

# page: ChromiumPage = ChromiumPage(addr_or_opts=co)

page: ChromiumPage = ChromiumPage(timeout=60)

logging.info("浏览器启动")


ac = Actions(page)

AD_url: str = "https://www.simcompanies.com/messages/"  # 新标签的地址

tab_messages: ChromiumTab = page.new_tab()  # 创建新标签

logging.info("创建第二标签")

# https://curlconverter.com/  curl转换工具

# web.get("https://www.simcompanies.com/zh/landscape/")

time.sleep(5)

logging.info("请登录，进入游戏主界面")



def number_str_to_float(txt: str, roundUp: int = 1) -> float:

    """将价格str转换为float

    :txt: 要转换的字符串,

    :roundUp: 转换后要除以的值，通常为100，将价格4643变为46.43
    """

    match1: list[str] = re.findall(r"[-?0-9]+\.?", txt)

    c = ""

    match2: str = c.join(match1)

    nums: float = float(match2)

    nums = round(nums, 2)

    return nums / roundUp



class Wares:

    """仓库类"""


    def __init__(self) -> None:

        self.number_of_item_types: list[ItemType] = []


    def get_all_types(self):

        """仓库获取所有物品信息"""

        eles1: list[ChromiumElement] = page.eles(

            'x://b[text()="运输单位"]/../../../../../div'
        )

        """仓库eles的list"""

        for ele in eles1:

            i_list: list[ChromiumElement | str] = ele.eles("x://img/..")

            for a in i_list:

                name: str = a.ele("x:/b").text

                price: str = a.ele("x:/span[1]").text

                numbers: str = a.ele("x:/span[2]").text

                logging.info("name:{},price:{},numbers:{}".format(name, price, numbers))



class ItemType:  # 物品类-物资的种类

    def __init__(self, name: str, num: float, price: float, id: str = "000") -> None:

        self.name: str = name  # 名称

        self.numbers: float = num  # 数量

        self.price: float = price  # 价格

        self.id: str = id  # 物品ID号



class BaseMain:

    def __init__(self, url: str = "https://www.simcompanies.com/landscape/") -> None:

        self.base_url: str = url  # 主基地页面

        """主基地页面"""

        self.closed_stores: list[ChromiumElement] = []

        """空闲的店铺"""

        self.shared_warehouse: dict = {"Diesel": 0, "Petrol": 0}

        """共享库存数量"""

        self.sell_hours: str = "4hr"

        """销售小时数"""


    # @pysnooper.snoop("file.log")

    def retail(self, name: str) -> None:

        """零售商店开始卖货"""

        count1: int = 0  # 计数器

        while 1:

            page_start_time: float = time.time()

            count1 += 1

            logging.debug("第{}次扫描商店状态".format(count1))

            self.get_closed_stores()

            if len(self.closed_stores) > 0:

                logging.info("点击序列0的商店开始")

                self.closed_stores[0].click()

                logging.info("点击序列0的商店结束")

                page.wait.load_start()

                page.wait.load_complete()

                time.sleep(5)

                # 获取库存

                try:

                    text1: str = page.ele(

                        'x://h3[text()="{}"]/../span'.format(name), timeout=60

                    ).text

                    nums: float = number_str_to_float(text1)  # 库存DIESEL数量

                    base_one.shared_warehouse[name] = nums  # 更新共享库存数量


                except ElementNotFoundError as e:


                    logging.warning(e, ";;;获取库存环节")

                    break


                if nums > 6000:  # 判断库存是否充足

                    # 输入XX小时销量，并找到合理价格


                    a1: str = page.ele(

                        'x://h3[text()="{}"]/../div'.format(name)

                    ).text  # 平均价格

                    a2: str = number_str_to_float(a1)

                    num1: float = round(float(a2) * 0.9, 2)  # 起步价

                    num2: float = round(float(a2) * 1.1, 2)  # 终止价

                    last_money: float = -9999999.9  # 上次的利润，临时变量

                    list1: list[dict] = []

                    try:

                        while num1 <= num2:  # 在价格区间

                            num1 = round(num1, 2)

                            # 输入销售数量 3hr
                            page.ele(

                                'x://h3[text()="{}"]/../../../../descendant::input[@placeholder="Quantity"]'.format(
                                    name
                                )
                            ).input(

                                vals=self.sell_hours

                            )  # 设定销售小时数，以确定销售数量

                            time.sleep(0.3)

                            # 输入销售价格
                            page.ele(

                                'x://h3[text()="{}"]/../../../../descendant::input[@placeholder="Price"]'.format(
                                    name
                                )

                            ).input(num1)

                            time.sleep(0.3)

                            page.ele(

                                'x://h3[text()="{}"]/../div/*'.format(name)

                            ).click()  # 点击问号显示利润计算页面

                            time.sleep(1)

                            _money = page.ele("x://table/tbody/tr[5]/td[3]").text

                            _money = number_str_to_float(_money)
                            page.ele(

                                'x://div[text()="Retailing breakdown"]/button'

                            ).click()  # 点击关闭利润计算页面

                            time.sleep(0.8)

                            dict2: dict = {"售价": num1, "利润总和": _money}

                            if last_money > _money:  # 判断利润总和是否增长

                                break
                            else:

                                last_money = _money

                                list1.append(dict2)


                            num1 += 0.2

                    except ElementNotFoundError as e:

                        logging.warning(e, ";;;价格区间出错啦")


                        break


                    if len(list1) > 0:

                        good_price: dict = max(list1, key=lambda x: x["利润总和"])

                        logging.info(

                            "销售{a},最佳利润是 {b}".format(

                                b=good_price, a=self.sell_hours

                            ),
                        )

                        # 输入X小时的最佳销PRICE
                        page.ele(

                            'x://h3[text()="{}"]/../../../../descendant::input[@placeholder="Price"]'.format(
                                name
                            )

                        ).input(good_price["售价"])

                        # 点击销售按钮
                        page.ele(

                            'x://h3[text()="{}"]/../../../../descendant::button[text()="Sell"]'.format(
                                name
                            )

                        ).click()
                    else:

                        break

                else:

                    # 库存不足时，先合同采购

                    # if self.buying_incoming_contracts():

                    # print("合同采购成功")


                    # else:

                    #     print("合同采购失败，进入交易所采购")

                    #     # 库存不足,且没有合同时，交易所采购

                    #     self.purchase_at_market_price()

                    logging.info("当前{}数量不足，退出内层循环".format(name))


                    break

            else:

                time.sleep(10)  # 间隔几秒扫描一次商店零售状态

                page_end_time: float = time.time()

                time2 = page_end_time - page_start_time

                if time2 > 300:  # 当在页面停留时间超过N秒后刷新页面

                    page.refresh()

                    page.wait.load_complete()


    # @pysnooper.snoop("file.log")

    def buying_incoming_contracts(self) -> bool:

        """从合同采购"""
        page.get(

            "https://www.simcompanies.com/headquarters/warehouse/incoming-contracts/",

            timeout=60,
        )

        page.wait.load_complete()

        time.sleep(5)

        # 合同定位，eles

        # //div[text()="Incoming contracts"]/../div[2]/div

        contracts: list[ChromiumElement] = page.eles(

            'x://div[text()="Incoming contracts"]/../div[2]/div', timeout=60
        )

        # 如果有合同

        if len(contracts) > 0:

            # TODO 创建列表 保存字典，便利每个元素加入字典，加入列表，选择价格最低且现金足够支付的合同购买

            dicts: list[dict] = []  # 存放每个合同信息

            # 当前现金数量

            _money: str = page.ele("x://nav/div/div/div[2]/div[1]/a/div/div[2]").text[

                1:

            ]

            _price: float = number_str_to_float(_money)

            # 统计所有合同信息

            for i in contracts:

                # 获取价格文本

                _pr = i.ele("x://b").text[1:]

                # 价格的数字

                _pr = number_str_to_float(_pr)

                # /div[3]//span

                # 一份合同的总价款

                sum_money: str = i.ele("x:/div[3]//span").text[1:]

                sum_money: float = number_str_to_float(sum_money)

                dict0 = {"ele": i, "price": _pr, "sum_money": sum_money}

                dicts.append(dict0)

            good_dict: dict = {"price": 789, "sum_money": 789789}

            # 判断当前最佳合同

            for d in dicts:

                # 当前现金足够支付的,价格最低的

                if d["price"] < good_dict["price"] and _price - d["sum_money"] > 0:

                    good_dict = d

            if good_dict["price"] != 789 and good_dict["price"] < 40:

                # 点击购买合同按钮

                ele2: ChromiumElement = good_dict["ele"]

                ele2.ele('x:/div[3]//a[@aria-label="Sign contract"]').click()

                print("点击购买{c1}".format(c1=good_dict))

                return True
            else:

                return False
        else:

            return False


    # @pysnooper.snoop("file.log")

    def purchase_at_market_price(self):

        """从交易所采购"""

        # i_want_buy:list[str] = ['DIESEL','汽油']  11是汽油，12是DIESEL
        page.get(

            "https://www.simcompanies.com/zh/market/resource/12/", timeout=60

        )  # 打开交易所-DIESEL

        # 输入当前金额可购买的数量

        _price: float = float(
            page.ele(

                # 商品最低价格

                "x://table/tbody/tr[1]/td[4]/div/span"

            ).text[1:]
        )

        # 如果市场价高于 40，不买，返回

        if _price >= 38:

            time.sleep(2)
            return

        _money: str = page.ele(

            # 现金数量

            "x://nav/div/div/div[2]/div[1]/a/div/div[2]"

        ).text[1:]

        _c: float = number_str_to_float(_money)

        _buy_nums: float = _c / _price

        if _buy_nums >= 1000:

            _buy_nums = 1000

        page.ele('x://button[text()="Buy"]/../../descendant::input').input(

            vals=int(_buy_nums)
        )

        time.sleep(1)

        page.ele('x://button[text()="Buy"]').click()


    # @pysnooper.snoop("file.log")

    def get_closed_stores(self):

        """获取基地内所有空闲商店"""

        wait_ele_loaded: str = 'x://*[@id="page"]/div/div/div[2]/div/a[5]/img[1]'


        if ("landscape" not in page.url) or (

            page.wait.ele_loaded(wait_ele_loaded, timeout=120) == False
        ):

            page.get(self.base_url, timeout=60)  # 打开主页面

        # 找到所有加油站

        petrol_stations: list[ChromiumElement] = page.eles(

            'x://span[text()="Gas station"]/../..', timeout=2
        )

        # 重置空闲加油站为0个

        self.closed_stores = []

        for i in petrol_stations:

            _a: list = i.eles('x:descendant::span[text()="Selling"]', timeout=0.2)

            _b: list = i.eles(

                'x://span[text()="Gas station"]/../..//b[text()="Rush for "]',

                timeout=0.2,
            )

            # //span[text()="Gas station"]/../..//b[text()="Rush for "]  升级状态

            if not (

                len(_a) > 0 or len(_b) > 0

            ):  # 如果没有'正在零售Selling'字样就加list

                self.closed_stores.append(i)

        if len(self.closed_stores) > 0:

            today = time.ctime()

            # print("空闲的商店有{}个，请注意,{}".format(len(self.closed_stores), today))

            logging.info("空闲的商店有{}个，请注意".format(len(self.closed_stores)))


    def place_AD(self):

        """发布广告"""

        AD_price: str = """

⭐0: $40.01 |⭐0: $40.19

⭐1: $40.25 |⭐1: $40.41

⭐2: $40.74 |⭐2: $40.85

⭐3: $41.00 |⭐3: $41.19

⭐4: $41.28 |⭐4: $41.39

⭐5: $41.47 |⭐5: $41.63

or @MP-2.5%

        """  # 发布广告-内嵌价格部分

        # 下面这段英文是采购升级建筑材料的

        # Buying :re-111: 100  @MP-2%

        AD_str: str = (
            """

Buying :re-12: -|---- :re-11: 

{}

-----------------


Please send me the contract directly,The quantity of each contract is 10K,I will buy it in 8 hours.

            """.format(

                AD_price
            )
        )

        AD_str_ZH: str = (
            """

收购 :re-12: --|----- :re-11:  

{}

请直接发合同，每个合同10K数量，我经常在线，最迟8小时内购买.

            """.format(

                AD_price
            )
        )

        messages_EN: str = 'x://a[@href="/messages/chatroom_Sales/"]'

        messages_ZH: str = 'x://a[@href="/messages/chatroom_[ZH] 交易-chatroom_Sales/"]'

        enter_xpath = "x://textarea/../../../../../../../../../../div[1]//textarea/../../..//button"  # 回车发送消息按钮

        sent_AD_xpath: str = (

            "x://textarea/../../../../../../../../../../div[1]//textarea"  # 输入文字的xpath
        )

        tab_messages.get(AD_url, timeout=60)


        time.sleep(5)

        # 点击英文交易频道

        tab_messages.ele(messages_EN).click()

        print("点击英文交易频道{}".format(time.ctime()))

        logging.info("点击英文交易频道")

        time.sleep(1)

        # 输入广告文字

        time.sleep(1)

        tab_messages.ele(sent_AD_xpath).input(vals=AD_str)

        time.sleep(1)

        # 点击回车发送广告

        tab_messages.ele(enter_xpath).click()

        logging.info("点击回车发送广告")

        time.sleep(10)

        # 点击中文交易频道

        print("点击中文交易频道{}".format(time.ctime()))

        logging.info("点击中文交易频道")

        time.sleep(1)

        tab_messages.ele(messages_ZH).click()

        # 输入广告文字

        time.sleep(1)

        tab_messages.ele(sent_AD_xpath).input(vals=AD_str_ZH)

        time.sleep(1)

        # 点击回车发送广告

        time.sleep(1)

        tab_messages.ele(enter_xpath).click()

        logging.info("点击回车发送广告")



base_one = BaseMain()



def Retail_sales_main(base1: BaseMain) -> None:

    """零售主函数

    参数1：实例化的主基地类
    """

    count0 = 0

    while count0 < 2000:

        count0 += 1

        print("第{}次运行base1.retail()  {}".format(count0, time.ctime()))

        logging.info("第{}次运行base1.retail() ".format(count0))

        base1.retail(name="Diesel")

        base1.retail(name="Petrol")



def AD_main(base1: BaseMain) -> None:

    """广告主函数发布

    参数1：实例化的主基地类
    """

    while 1:

        randint_1 = random.randint(61, 81)  # 设置间隔时间区间

        base1.place_AD()

        print("距离下次发布广告随机间隔为{}分钟   {}".format(randint_1, time.ctime()))

        logging.info("距离下次发布广告随机间隔为{}分钟  ".format(randint_1))

        time.sleep(60 * randint_1)  # 间隔随机分钟发送一次



def main():

    """程序启动主函数"""

    with ThreadPoolExecutor(max_workers=2) as tpool:

        futures: list = [

            tpool.submit(Retail_sales_main, base_one),

            tpool.submit(AD_main, base_one),  # 发布广告，屏蔽即可关闭

        ]

    futures_wait(futures, return_when="ALL_COMPLETED")



if __name__ == "__main__":
    main()

